//
//  HourlyForecastTableViewCell.swift
//  SwiftWeather
//
//  Created by shen on 2023/8/28.
//

import UIKit
import Combine

final class HourlyForecastCellViewModel {
    // MARK: Properties

    @Published private var hour: Hour?

    var temperaturePublisher: AnyPublisher<Temperature, Never> {
        $hour.combineLatest($temperatureUnits)
            .compactMap { hour, units in
                guard let hour else { return nil }
                return (hour, units)
            }
            .flatMap { hour, units in
                Just(hour).mapToTemperature(in: units)
            }
            .eraseToAnyPublisher()
    }

    var hourPublisher: AnyPublisher<String, Never> {
        $hour
            .compactMap {
                $0?.time?.convertToDate(format: DateFormat.hourlyWeatherForecast.rawValue)
            }
            .map { $0.toString(format: DateFormat.hour.rawValue) }
            .eraseToAnyPublisher()
    }

    var conditionPublisher: AnyPublisher<String, Never> {
        $hour
            .compactMap { $0?.condition?.text }
            .eraseToAnyPublisher()
    }

    var weatherIconPublisher: AnyPublisher<(code: Int, dayPeriod: DayPeriod), Never> {
        codePublisher.combineLatest(dayPeriodPublisher)
            .map { (code: $0.0, dayPeriod: $0.1) }
            .eraseToAnyPublisher()
    }

    private var dayPeriodPublisher: AnyPublisher<DayPeriod, Never> {
        $hour
            .compactMap { $0?.isDay }
            .compactMap({ DayPeriod(rawValue: $0) })            
            .eraseToAnyPublisher()
    }

    private var codePublisher: AnyPublisher<Int, Never> {
        $hour
            .compactMap { $0?.condition?.code }
            .eraseToAnyPublisher()
    }

    @Published private var temperatureUnits: TemperatureUnits

    // MARK: - Initialization

    init(hour: Hour?, temperatureUnits: TemperatureUnits) {
        self.hour = hour
        self.temperatureUnits = temperatureUnits
    }
}

class HourlyForecastTableViewCell: BaseTableViewCell, ViewModelable {
    typealias ViewModel = HourlyForecastCellViewModel

    // MARK: Properties

    var viewModel: ViewModel! {
        didSet {
            viewModel.conditionPublisher
                .assignToTextOnLabel(conditionLabel)
                .store(in: &cancellables)

            viewModel.hourPublisher
                .assignToTextOnLabel(hourLabel)
                .store(in: &cancellables)

            viewModel.weatherIconPublisher
                .sink { [weak self] code, dayPeriod in
                    self?.weatherIconImageView.image = Resources.Images.Weather.weatherIcon(
                        code: code,
                        dayPriod: dayPeriod
                    )
                }
                .store(in: &cancellables)

            viewModel.temperaturePublisher
                .map { $0.stringFormat }
                .assignToTextOnLabel(temperatureLabel)
                .store(in: &cancellables)
        }
    }

    private var cancellables = Set<AnyCancellable>()

    // MARK: - Views

    private lazy var mainHStack = UIStackView(
        arrangedSubviews: [hourLabel, weatherInfoHStack],
        axis: .horizontal,
        spacing: 20,
        alignment: .center
    )

    private lazy var hourLabel = UILabel(font: Resources.Fonts.system(weight: .semibold))

    private lazy var weatherInfoHStack = UIStackView(
        arrangedSubviews: [weatherIconImageView, conditionLabel, temperatureLabel],
        axis: .horizontal,
        spacing: 20,
        alignment: .center,
        distribution: .fillProportionally
    )

    private lazy var weatherIconImageView = UIImageView(contentMode: .scaleAspectFit)

    private lazy var conditionLabel = UILabel(
        font: Resources.Fonts.system(size: 14, weight: .light)
    )

    private lazy var temperatureLabel: UILabel = {
        let label = UILabel(
            textColor: .white,
            font: Resources.Fonts.system(weight: .semibold),
            textAlignment: .center
        )
        label.backgroundColor = .systemIndigo
        label.layer.masksToBounds = true
        return label
    }()

    // MARK: - Methods

    override func layoutSubviews() {
        temperatureLabel.layer.cornerRadius = temperatureLabel.frame.height / 2
    }

    override func setupViews() {
        contentView.addSubview(mainHStack, useAutoLayout: true)
    }

    override func constraintViews() {
        NSLayoutConstraint.activate([
            hourLabel.widthAnchor.constraint(equalToConstant: 50),

            temperatureLabel.widthAnchor.constraint(equalToConstant: 60),
            temperatureLabel.heightAnchor.constraint(equalToConstant: 30),

            weatherIconImageView.widthAnchor.constraint(equalToConstant: 25),
            weatherIconImageView.heightAnchor.constraint(equalTo: weatherIconImageView.widthAnchor),

            mainHStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
            mainHStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            mainHStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            mainHStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
        ])
    }
}
